package com.scs.application.core.utils;

import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.exception.ValidException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.NetworkInterface;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/***
* @Description： javax的一些工具类
*/
@Slf4j
public class UtilJavax {
	/**获取类成员变量
	 * @param objectEntity
	 * @return [{"fielName"}]
	 */
	public static   JSONArray getClassFields(Class<?> objectEntity) {
		JSONArray fieldArr =new JSONArray();
		String entityClzzName=objectEntity.getName();
		try {
			Class<?> clz = Class.forName(entityClzzName).newInstance().getClass();
			//遍历对象属性
			Object fieldName="";
			JSONObject jsonObject=null;
			for (Field field : clz.getDeclaredFields()){
				fieldName=field.getName();
				if(fieldName.equals("serialVersionUID")) continue;
				jsonObject=new JSONObject();
				jsonObject.put("fieldName", fieldName);
				fieldArr.add(jsonObject);
			}
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return fieldArr;
	}



	/**根据clazz全限定名称获取对象实例
	 * @param clazzFullName
	 * @return
	 */
	public static Class<?> getClassByNameAll(String clazzFullName) {
		Class<?> c=null;
		try {
			c =Class.forName(clazzFullName) ;
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return c;
	}

	/**根据clazz全限定名称获取对象实例，即调用空构造函数
	 * @param clazzFullName	类全限定名称
	 * @return	null:调用成功，非null：异常信息
	 */
	public static String newClassInstance(String clazzFullName) {
		String ret=null;
		try {
			Class.forName(clazzFullName).newInstance();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			ret="实例化异常，类名："+clazzFullName;
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			ret="非法访问异常，类名："+clazzFullName;
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			ret="类未找到异常，类名："+clazzFullName;
		}
		return ret;
	}

	 /**
     * 转换成十六进制字符串
     * @param username
     * @return
     */
    public static byte[] hex(String key){
        String f = DigestUtils.md5Hex(key);
        byte[] bkeys = new String(f).getBytes();
        byte[] enk = new byte[24];
        for (int i=0;i<24;i++){
            enk[i] = bkeys[i];
        }
        return enk;
    }

    /**
     * 3DES加密
     * @param key 密钥
     * @param srcStr 将加密的字符串
     * @return
     *
     */
    public static String  des3Encode(String key,String srcStr){
    	byte[] keybyte = hex(key);
    	byte[] src = srcStr.getBytes();
        try {
           //生成密钥
           SecretKey deskey = new SecretKeySpec(keybyte, "DESede");
           //加密
           Cipher c1 = Cipher.getInstance("DESede");
           c1.init(Cipher.ENCRYPT_MODE, deskey);

           String pwd = Base64.encodeBase64String(c1.doFinal(src));
           return pwd;
       } catch (java.security.NoSuchAlgorithmException e1) {
            e1.printStackTrace();
       }catch(javax.crypto.NoSuchPaddingException e2){
           e2.printStackTrace();
       }catch(Exception e3){
           e3.printStackTrace();
       }
       return null;
   }

   /**
    * 3DES解密
    * @param key 加密密钥
    * @param desStr 加密后的字符串
    * @return
    */
    public static String des3Decode(String key, String desStr){
    	Base64 base64 = new Base64();
    	byte[] keybyte = hex(key);
    	byte[] src = base64.decode(desStr);

        try {
            //生成密钥
            SecretKey deskey = new SecretKeySpec(keybyte, "DESede");
            //解密
            Cipher c1 = Cipher.getInstance("DESede");
            c1.init(Cipher.DECRYPT_MODE, deskey);
            String pwd = new String(c1.doFinal(src));
            return pwd;
        } catch (java.security.NoSuchAlgorithmException e1) {
            // TODO: handle exception
            e1.printStackTrace();
        }catch(javax.crypto.NoSuchPaddingException e2){
            e2.printStackTrace();
        }catch(Exception e3){
            e3.printStackTrace();
        }
        return null;
    }

	/**
	 * 获取堆栈异常所有信息
	 * 请勿保存到数据库，因为数据长度不定
	 *
	 * @return 堆栈异常所有信息
	 */
	public static String getStackTrace(Exception e) {
		String err = "出现了异常，但没有有效异常堆栈";
		if(e==null) return err;
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		e.printStackTrace(pw);
		err = sw.toString();
		try {
			sw.close();
			sw = null;
		} catch (IOException e1) {
		}
		try {
			pw.close();
			pw = null;
		} catch (Exception e1) {
			// TODO Auto-generated catch block
		}
		return err;
	}

	/**base64 编码
	 * @param s 	未编码的字符串
	 * @param isZip	true:编码后进行zip压缩
     * @return 出现异常返回""
	 * @throws UnsupportedEncodingException
     */
    public static String base64Encode(String s,boolean isZip) throws UnsupportedEncodingException {
        if (s == null) return null;
        String res= java.util.Base64.getEncoder().encodeToString(s.getBytes("UTF-8"));
        return res;
    }

    /**base64 解码
	 * @param s 	未编码的字符串
	 * @param isZip	字符串为zip压缩后的字符串，那么将先解压缩，再解密
     * @throws UnsupportedEncodingException
     *
     */
    public static String base64Decode(String s,boolean isUnzip) throws UnsupportedEncodingException {
        if (s == null) return null;
        String ret=new String(java.util.Base64.getDecoder().decode(s.getBytes("GBK")));
        return ret;
    }
    /**url编码
     * @param 	str
     * @param 	enc，默认编码格式 utf-8
     * @throws UnsupportedEncodingException
     *
     */
    public static String urlEncode(String str,String enc) throws UnsupportedEncodingException {
    	if(UtilCommon.strIsNull(enc)) enc="utf-8";
    	return URLEncoder.encode(str, enc);
    }

    /**url解码
     * @param 	str
     * @param 	enc，默认编码格式 utf-8
     * @throws UnsupportedEncodingException
     *
     */
    public static String urlDecode(String str,String enc) throws UnsupportedEncodingException {
    	if(UtilCommon.strIsNull(enc)) enc="utf-8";
    	return URLDecoder.decode(str, enc);
    }

    /**
     * 使用zip进行压缩
     *
     * @param str
     *            压缩前的文本
     * @return 返回压缩后的文本
     */
    public static final String zip(String str) {
        if (str == null)
            return null;
        byte[] compressed;
        ByteArrayOutputStream out = null;
        ZipOutputStream zout = null;
        String compressedStr = null;
        try {
            out = new ByteArrayOutputStream();
            zout = new ZipOutputStream(out);
            zout.putNextEntry(new ZipEntry("0"));
            zout.write(str.getBytes("utf-8"));
            zout.closeEntry();
            compressed = out.toByteArray();
            compressedStr = new String(new Base64().encode(compressed),"utf-8");
        } catch (IOException e) {
            compressed = null;
        } finally {
            if (zout != null) {
                try {
                    zout.close();
                } catch (IOException e) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                }
            }
        }
        return compressedStr.replace("\n","");
    }

    /**
     * 使用zip进行解压缩
     *
     * @param compressed
     *            压缩后的文本
     * @return 解压后的字符串
     */
    public static final String unzip(String str) {
        if (str == null) {
            return null;
        }

        ByteArrayOutputStream out = null;
        ByteArrayInputStream in = null;
        ZipInputStream zin = null;
        String decompressed = null;
        try {
            byte[] compressed = new Base64().decode(str.getBytes("utf-8"));
            out = new ByteArrayOutputStream();
            in = new ByteArrayInputStream(compressed);
            zin = new ZipInputStream(in);
            zin.getNextEntry();
            byte[] buffer = new byte[1024];
            int offset = -1;
            while ((offset = zin.read(buffer)) != -1) {
                out.write(buffer, 0, offset);
            }
            decompressed = out.toString("utf-8");
        } catch (IOException e) {
            decompressed = null;
        } finally {
            if (zin != null) {
                try {
                    zin.close();
                } catch (IOException e) {
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                }
            }
        }
        return decompressed;
    }
    /**字符串转换unicode
     * 方式说明:
     */
    public static String unicodeEncode(String string) {
        StringBuffer unicode = new StringBuffer();
        for (int i = 0; i < string.length(); i++) {
            char c = string.charAt(i);  // 取出每一个字符
            unicode.append("\\u" +Integer.toHexString(c));// 转换为unicode
        }
        return unicode.toString();
    }

    /**unicode 转字符串
     * 方式说明:
     */
    public static String unicodeDecode(String unicode) {
        StringBuffer string = new StringBuffer();
        String[] hex = unicode.split("\\\\u");
        for (int i = 1; i < hex.length; i++) {
            int data = Integer.parseInt(hex[i], 16);// 转换出每一个代码点
            string.append((char) data);// 追加成string
        }
        return string.toString();
    }
    /**
     * 字符串转换为Ascii
     * @param value
     * @return
     */
    public static String stringToAscii(String value)
    {
        StringBuffer sbu = new StringBuffer();
        char[] chars = value.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if(i != chars.length - 1)
            {
                sbu.append((int)chars[i]).append(",");
            }
            else {
                sbu.append((int)chars[i]);
            }
        }
        return sbu.toString();
    }

    /**
     * Ascii转换为字符串
     * @param value
     * @return
     */
    public static String asciiToString(String value)
    {
        StringBuffer sbu = new StringBuffer();
        String[] chars = value.split(",");
        for (int i = 0; i < chars.length; i++) {
            sbu.append((char) Integer.parseInt(chars[i]));
        }
        return sbu.toString();
    }

    /**获取id
     */
    public static String getIP(HttpServletRequest request) {
    	String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
    /**
	 *
	 * @param ip 目标ip,一般在局域网内
	 * @param sourceString 命令处理的结果字符串
	 * @param macSeparator mac分隔符号
	 * @return mac地址，用上面的分隔符号表示
	 */
	public static String filterMacAddress(final String ip, String sourceString, final String macSeparator) {
		String result = "";
		int index = sourceString.indexOf(ip);
		if (index == -1) {
			index = 0;
		}
		sourceString = sourceString.substring(index, sourceString.length() - 1);
		String regExp = "((([0-9,A-F,a-f]{1,2}" + macSeparator + "){1,5})[0-9,A-F,a-f]{1,2})";
		Pattern pattern = Pattern.compile(regExp);
		Matcher matcher = pattern.matcher(sourceString);
		while (matcher.find()) {
			result = matcher.group(1);
			if (sourceString.indexOf(ip) <= sourceString.lastIndexOf(matcher.group(1))) {
				break; // 如果有多个IP,只匹配本IP对应的Mac.
			}
		}
		return result;
	}
	  /**
     * 命令获取mac地址
     * @param cmd
     * @return
     */
    private static String callCmd(String[] cmd) {
	  String result = "";
	  String line = "";
	    try {
	        Process proc = Runtime.getRuntime().exec(cmd);
	        InputStreamReader is = new InputStreamReader(proc.getInputStream());
	        BufferedReader br = new BufferedReader (is);
	        while ((line = br.readLine ()) != null) {
	             result += line;
	        }
	   }catch(Exception e) {
	        e.printStackTrace();
	   }
	      return result;
    }
	/**
	 * @param cmd
	 *            第一个命令
	 * @param another
	 *            第二个命令
	 * @return 第二个命令的执行结果
	 *
	 */

	private static String callCmd(String[] cmd,String[] another) {
	   String result = "";
	   String line = "";
	   try {
	      Runtime rt = Runtime.getRuntime();
	      Process proc = rt.exec(cmd);
	      proc.waitFor(); // 已经执行完第一个命令，准备执行第二个命令
	      proc = rt.exec(another);
	      InputStreamReader is = new InputStreamReader(proc.getInputStream());
	      BufferedReader br = new BufferedReader (is);
	      while ((line = br.readLine ()) != null) {
	         result += line;
	      }
	   }catch(Exception e) {
	        e.printStackTrace();
	   }
	      return result;
	}
	/**
	 * @param ip 目标ip
	 * @return Mac Address
	 */
	public static String getMacInWindows(final String ip) {
		String result = "";
		String[] cmd = { "cmd", "/c", "ping " + ip };
		String[] another = { "cmd", "/c", "arp -a" };
		String cmdResult = callCmd(cmd, another);
		result = filterMacAddress(ip, cmdResult, "-");
		return result;
	}

	/**
	 * @param ip 目标ip
	 * @return Mac Address
	 *
	 */
	public static String getMacInLinux(final String ip) {
		String result = "";
		String[] cmd = { "/bin/sh", "-c", "ping " + ip + " -c 2 && arp -a" };
		String cmdResult = callCmd(cmd);
		result = filterMacAddress(ip, cmdResult, ":");

		return result;
	}
    /**获取MAC根据IP
     */
    public static String getMAC(String ip) throws Exception {
    	String macAddress = "";
		macAddress = getMacInWindows(ip).trim();
		if (macAddress == null || "".equals(macAddress)) {
			macAddress = getMacInLinux(ip).trim();
		}
		return macAddress;
    }
    /**
     *  获取本地mac地址
     */
    public static String getMacLocal() {
        try {
            Enumeration<NetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces();
            byte[] mac = null;
            String displayName="";
            while (allNetInterfaces.hasMoreElements()) {
                NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
                displayName=netInterface.getDisplayName();
                if (displayName.contains("wlan") || netInterface.isLoopback() || netInterface.isVirtual() || netInterface.isPointToPoint() || !netInterface.isUp() ) {
                    continue;
                } else {
                    mac = netInterface.getHardwareAddress();
                    if (mac != null) {
                        StringBuilder sb = new StringBuilder();
                        for (int i = 0; i < mac.length; i++) {
                            sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
                        }
                        if (sb.length() > 0) {
                            return sb.toString();
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**截取多长字符串
     * @param str
     * @param str
     * @return
     */
    public static String getStringPre(String strs,int length) {
        int sum = 0;
        String finalStr = "";
        if (null == strs || strs.getBytes().length <= length) {
            finalStr = (strs==null?"":strs);
        } else {
            for (int i = 0; i < strs.length(); i++) {
                String str = strs.substring(i, i + 1);
                // 累加单个字符字节数
                sum += str.getBytes().length;
                if (sum > length) {
                    finalStr = strs.substring(0, i) + "...";
                    break;
                }
            }
        }
        return finalStr;
    }

    /**
    * @Description：根据HttpServletRequest获取用户真实IP地址
    */
    public static String getIPAddress(HttpServletRequest request) {
        String ip = null;

        //X-Forwarded-For：Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //Proxy-Client-IP：apache 服务代理
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //WL-Proxy-Client-IP：weblogic 服务代理
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //HTTP_CLIENT_IP：有些代理服务器
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }

        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //X-Real-IP：nginx服务代理
            ipAddresses = request.getHeader("X-Real-IP");
        }

        //有些网络通过多层代理，那么获取到的ip就会有多个，一般都是通过逗号（,）分割开来，并且第一个ip为客户端的真实IP
        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }

        //还是不能获取到，最后再通过request.getRemoteAddr();获取
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
    /**
     * 是否jar包启动
     * @return true: jar包启动
     */
    public static boolean isRunJar() {
        String protocol = UtilJavax.class.getClassLoader().getResource("").getProtocol();
        return  protocol.startsWith("jar");
    }
    /**
     * 安全转换反射返回的对象列表
     */
    public static <T> List<T> safeConvertList(Object result, Class<T> elementType) {
        List<T> safeList = new ArrayList<>();
        if (result == null) {
            return safeList;
        }
        Object obj = null,objNew;
        try {
            obj = elementType.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (result instanceof List) {
            for (Object item : (List<?>) result) {
                if (elementType.isInstance(item)) {
                    safeList.add(elementType.cast(item));
                } else if(obj != null && obj.getClass().getName().equals(item.getClass().getName())) {
                    try {
                        objNew = elementType.newInstance();
                        BeanUtils.copyProperties(item, objNew);
                        safeList.add((T) objNew);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return safeList;
    }

    /**
     * 反射调用并安全转换对象列表
     */
    public static <T> List<T> invokeAndConvertList(Object obj, String methodName, Class<T> elementType,Object ... funParam) {

        try {
            Object result = ReflectUtil.invoke(obj, methodName,funParam);
            return safeConvertList(result, elementType);
        }catch (Exception e) {
            if (e.getCause() != null && e.getCause() instanceof InvocationTargetException) {
                InvocationTargetException invocationTargetException = (InvocationTargetException) e.getCause();
                if (invocationTargetException != null && invocationTargetException.getTargetException() != null
                ) {
                    BusinessException.throwError(invocationTargetException.getTargetException().getMessage());
                }
            }else{
                BusinessException.throwError(e.getMessage());
            }
            return null;
        }
    }

}
